package engine.gui;

import static org.lwjgl.opengl.GL11.*;

import java.awt.Font;
import java.awt.FontFormatException;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;

public class BitmapFont implements engine.gui.Font {
	private static final int NUM_BASE = 32;
	private static final int NUM_MAX = 256 - 32;
	
	private int baseList;
	private int fontSize;
	private int[] widths;
//	private FontMetrics fontMetrics;
	
	private double ascend;
	private double descend;

	public BitmapFont(String fontName, int fontSize, boolean external) {
		this.fontSize = fontSize;
		buildFont(fontName, fontSize, external);
	}

	private void buildFont(String fontName, int fontSize, boolean external) {
		baseList = glGenLists(NUM_MAX);

		Font font = null;

		if (external) {
			try {
				InputStream inputStream = this.getClass().getResourceAsStream(
						"/" + fontName);
				font = Font.createFont(Font.TRUETYPE_FONT, inputStream)
						.deriveFont((float)fontSize);
			} catch (FontFormatException e) {
				e.printStackTrace();
			} catch (IOException e) {
				System.out.println(fontName + " could not be found!");
				font = new Font("serif", Font.PLAIN, 20);
			}
		} else {
			font = new Font(fontName, Font.PLAIN, fontSize);
		}

		buildDisplayLists(font, NUM_BASE, NUM_MAX, baseList);
		
		createDescendAndAscend(font);
	}

	private void buildDisplayLists(Font font, int firstCharacter, int count,
			int base) {
		glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT);
		glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

		BufferedImage theImage;
		int listNumber = base;
		FontMetrics fontMetrics = getFontMetrics(font);
		
		widths = fontMetrics.getWidths();
		int height = fontMetrics.getHeight();

		for (int currentUnichar = firstCharacter; currentUnichar < firstCharacter
				+ count; currentUnichar++, listNumber++) {
			char currentCharacter = (char) currentUnichar;

			if (widths[currentUnichar] > 0 && height > 0) {
				theImage = new BufferedImage(widths[currentUnichar] + 1, height,
						BufferedImage.TYPE_4BYTE_ABGR);
				Graphics2D g2d = theImage.createGraphics();
				g2d.setFont(font);
				g2d.drawString(currentCharacter + "", 0,
						fontMetrics.getAscent());
				g2d.dispose();

				makeListFromImage(listNumber, theImage);
			}
		}

		glPopClientAttrib();
	}
	
	private void createDescendAndAscend(Font font) {
		BufferedImage buf = new BufferedImage(1, 1, BufferedImage.TYPE_4BYTE_ABGR);
		Graphics2D g = buf.createGraphics();
		FontRenderContext frc = g.getFontRenderContext();
		GlyphVector gv = font.createGlyphVector(frc, "B");
		Shape shape = gv.getOutline();
		
		ascend = shape.getBounds2D().getHeight();
		
		gv = font.createGlyphVector(frc, "g");
		shape = gv.getOutline();
		descend = shape.getBounds2D().getY();
	}
	
	public double getAscend() {
		return ascend;
	}
	
	public double getDescend() {
		return descend;
	}
	
	public void dealloc() {
		glDeleteLists(baseList, NUM_MAX);
	}

	private FontMetrics getFontMetrics(Font font) {
		BufferedImage buf = new BufferedImage(1, 1,
				BufferedImage.TYPE_BYTE_GRAY);
		Graphics g = buf.getGraphics();
		return g.getFontMetrics(font);
	}
	
	private void makeListFromImage(int list, BufferedImage img) {
		glNewList(list, GL_COMPILE);
		glBegin(GL_POINTS);
		for(int x = 0; x < img.getWidth() - 1; x++) {
			for(int y = 1; y < img.getHeight() - 1; y++) {
				if(img.getRGB(x, y) != 0) {
					glVertex2i(x, y - getHeight());
				}
			}
		}
		glEnd();
		glEndList();
	}
	
	@Override
	public void draw(String text, double posX, double posY) {
		glPushMatrix();
		{
			glLoadIdentity();
			glTranslated(posX, posY, 0);
			for(int i = 0; i < text.length(); i++) {
				glCallList(baseList + text.charAt(i) - NUM_BASE);
				glTranslatef(widths[text.charAt(i)], 0, 0);
			}	
		}
		glPopMatrix();
	}
	
	public int getHeight() {
		return fontSize;
	}
	
	public double getWidth(String text) {
		int length = 0;
		
		for(int i = 0; i < text.length(); i++) {
			length += widths[text.charAt(i)];
		}
		
		return length;
	}

	public void drawTextToScreen(String str, float x, float y, int screenSizeX,
			int screenSizeY) {
		boolean enableLight = false;
		
		if(glIsEnabled(GL_LIGHTING)) {
			enableLight = true;
			glDisable(GL_LIGHTING);
		}
		
		boolean enableTexture = false;
		
		if(glIsEnabled(GL_TEXTURE_2D)) {
			enableTexture = true;
			glDisable(GL_TEXTURE_2D);
		}
		
		glMatrixMode(GL_PROJECTION);
		glPushMatrix();
		{
			glLoadIdentity();
			glOrtho(0, screenSizeX, 0, screenSizeY, -1, 1);
			glMatrixMode(GL_MODELVIEW);
			glPushMatrix();
			{
				glLoadIdentity();
				glRasterPos2f(x, screenSizeY - y - fontSize);
				glPushAttrib(GL_LIST_BIT);
				{						
					for(int i = 0; i < str.length(); i++) {
						glCallList(baseList + str.charAt(i) - NUM_BASE);
					}	
				}
				glPopAttrib();
			}
			glPopMatrix();
			glMatrixMode(GL_PROJECTION);
		}
		glPopMatrix();
		glMatrixMode(GL_MODELVIEW);
		
		if(enableLight) {
			glEnable(GL_LIGHTING);
		}
		
		if(enableTexture) {
			glEnable(GL_TEXTURE_2D);
		}
	}
}
